#include "header\Animal.h"
#include "header\Ecosystem.h"

//State Machine Constants Definition. Each state of the machine gets a unique number.
#define _NEW_BORN_    -1
#define _ESCAPE_     0
#define _PURSUE_     1
#define _REPRODUCE_ 2
#define _MOVE_        3
#define _EAT_        4

//Moving directions are represented by constants valued such as a combined direction is the sum of the two cardinals directions
#define _NORTH_         1
#define _NORTH_EAST_     3
#define _EAST_             2
#define _SOUTH_EAST_    6
#define _SOUTH_            4
#define _SOUTH_WEST_    11
#define _WEST_            7
#define _NORTH_WEST_    8
#define _NULL_            0

int verif(Ecosystem const& eco, int x) {
    if (x <= 0)
        return 0;
    else if (x >= eco.w())
        return eco.w();
    return x;
}

void Animal::lookAround() {
    peeked = eco.getEntitiesAt(position.X(), position.Y(), fov); // Returns a vector of pointers of Entity stored in the "peeked" vector
}

int Animal::getTokens() const {
    return tokens;
}

void Animal::update() {
    int nbPeeked;
    bool flag;
    int actionsCounter = getTokens(); //Counts the number of actions performed by this animal on its round
    cout << "I'm a " << race << endl;
    
    if (lifePoints <= 0) //If the animals begins it's round with not enough life points, it is eliminated
        eco.killEntityAt(position.X(), position.Y(), getTypeEntity());

    int state = _NEW_BORN_; //The animal begins its round with no default state
    Entity* target;

    while (actionsCounter) { //While the animal have sufficient Action Tokens, it's able to do several actions in the state machine main loop
        lookAround();
        nbPeeked = peeked.size();
        flag = true;
        
        for (int cptr1 = 0; cptr1 < nbPeeked; cptr1++) { //We're examining each element the animal has seen
            target = peeked.at(cptr1);

            if (eco.mapPredator().isPredator(target->getRace(), getRace())) { //If this animal is one of our predator
                 //target contains the reference to escape from
                state = _ESCAPE_;
                break; //Breaks the loop and launches the state machine in the "escape" state, therefore trying to escape from the first animal found in sight; not the closest one
            } else if (getRace() == target->getRace()) { //If this animal has seen a fellow...
                if (rand() % 100 < pReproduce) //And succeed to its reproducing test
                {
                    state = _REPRODUCE_; //it's able to reproduce
                }
            } else if (eco.mapPredator().isPrey(target->getRace(), getRace())) { //If this animal has not seen any predator, we're looking for a prey
                state = _PURSUE_;
            } else {
                while (direction == 0 || direction == 5 || direction == 9 || direction == 10)
                    direction = rand() % 11;
                state = _MOVE_;
            }
        }

        while (flag) { //We're obliged to use a flag to stop the state machine. While this flag is down, the machine continues it's execution
                switch (state) {
                    case _ESCAPE_:
                    cout << "I'm a " << race << " and I'm Escaping from (" << getPosition().X() << "," << getPosition().Y() << ")" << endl;
                        direction = _NULL_;
                        if (position.X() > target->getPosition().X()) //If the target is located on the left of the current animal, its heads towards the EAST
                            direction = _EAST_;
                        else if (position.X() < target->getPosition().X()) //If the target is located on the right of the current animal, its heads towards the WEST
                            direction = _WEST_;
                        if (position.Y() > target->getPosition().Y()) //If the target is located on top of the current animal, its heads towards the SOUTH
                            direction = _SOUTH_ + direction; //Here a combination of the EAST or WEST or NULL set direction is applied in order to go in the best direction
                        else if (position.Y() < target->getPosition().Y()) //If the target is located under the current animal, its heads towards the NORTH
                            direction = _NORTH_ + direction; //Here a combination of the EAST or WEST or NULL set direction is applied in order to go in the best direction
                        state = _MOVE_;
                        break;

                    case _PURSUE_:
                    cout << "I'm a " << race << " and I'm Chasing a " << target->getRace() << " from (" << getPosition().X() << "," << getPosition().Y() << ")" << endl;
                        direction = _NULL_;
                        if (position.X() < target->getPosition().X()) //If the target is located on the right of the current animal, its heads towards the EAST
                            direction = _EAST_;
                        else if (position.X() > target->getPosition().X()) //If the target is located on the left of the current animal, its heads towards the WEST
                            direction = _WEST_;
                        if (position.Y() < target->getPosition().Y()) //If the target is located under the current animal, its heads towards the SOUTH
                            direction = _SOUTH_ + direction; //Here a combination of the EAST or WEST or NULL set direction is applied in order to go in the best direction
                        else if (position.Y() > target->getPosition().Y()) //If the target is located on top of the current animal, its heads towards the NORTH
                            direction = _NORTH_ + direction; //Here a combination of the EAST or WEST or NULL set direction is applied in order to go in the best direction
                        if (position.X() == target->getPosition().X() && position.Y() == target->getPosition().Y()) //If the animal is able to reach the prey position, it'll eat it...
                            state = _EAT_;
                        else
                            state = _MOVE_;
                        break;

                    case _REPRODUCE_:
                    cout << "I'm a " << race << " and I'm reproducing with a " << target->getRace() << " at (" << target->getPosition().X() << "," << target->getPosition().Y() << ")"  << endl;
                        eco.addAnimal(race, pEscape, pMove, pReproduce, tokens, maxLife, fov);
                        actionsCounter--; //Reproducing consumes and action

                        flag = !flag; //The actionCounter had been affected, we're breaking the state machine loop
                        break;

                    case _EAT_:
                    cout << "I'm a " << race << " and I'm eatin' a " << target->getRace() << " at (" << target->getPosition().X() << "," << target->getPosition().Y() << " and getting back all of mah life points! Nom nom nom..." << endl;
                        actionsCounter--; //Each eating action consumes an action token
                        target->setLifePoints(0); //The target gets deadly wounded
                        setLifePoints(maxLife); //The current animal gets its Life point back at maximum

                        flag = !flag; //The actionCounter had been affected, we're breaking the state machine loop
                        break;


                    case _MOVE_:
                        while (rand() % 100 > pMove && actionsCounter) { //Since the animal is moving, it's able to charge in a direction without looking around anymore.
                            if (direction == _NORTH_)
                                position.position(verif(eco, position.X() + 1), verif(eco, position.Y()));
                            else if (direction == _NORTH_EAST_)
                                position.position(verif(eco, position.X() + 1), verif(eco, position.Y() + 1));
                            else if (direction == _EAST_)
                                position.position(verif(eco, position.X()), verif(eco, position.Y() + 1));
                            else if (direction == _SOUTH_EAST_)
                                position.position(verif(eco, position.X() - 1), verif(eco, position.Y() + 1));
                            else if (direction == _SOUTH_)
                                position.position(verif(eco, position.X() - 1), verif(eco, position.Y()));
                            else if (direction == _SOUTH_WEST_)
                                position.position(verif(eco, position.X() - 1), verif(eco, position.Y() - 1));
                            else if (direction == _WEST_)
                                position.position(verif(eco, position.X()), verif(eco, position.Y() - 1));
                            else if (direction == _NORTH_WEST_)
                                position.position(verif(eco, position.X() + 1), verif(eco, position.Y() - 1));
                            actionsCounter--; //Each movement consumes an action token.
                        }
                        cout << "I'm a " << race << " and I'm arriving at (" << getPosition().X() << "," << getPosition().Y() << ")" << endl;
                        flag = !flag; //Since the actions counter had been modified -or emptied- the flag is changed so the animal is able to look around once again.
                        break;

                    case _NEW_BORN_:
                        // Idle....
                        actionsCounter = 0;
                        flag = !flag;
                        break;
                }
        }
    }
}
